/**
 * #info
 *@author    wuwg
 *@createTime   2018/03/20
 *@updateTime    2018/06/23
 *@description   tree  component 树组件
 */
/**
 * #item
 *
 * @type        aty-tree
 * @cn          树
 * @menu        导航
 * @order       20
 * @stPlugin    true
 * @stContainer false
 * @description  树控件，直接单选、多选、后台过滤、前台过虑
 * @mixins [./components/components/navigation/tree/store/index.js]
 * @mixins [./components/components/navigation/tree/tree-node/index.js]
 */
import config from "../../../config/config.js"

import locale from "../../../mixins/locale.js"

// components
import atyTreeNode from './tree-node/index.vue'
import autoComplete from '../../form/auto-complete/index.vue'
import atyButton from '../../basic/button/index.vue'
// mixins
import store from './store/index.js'
//  prefix
const prefixCls = config.prefix + '-tree'


// module
export default {
  name: prefixCls,
  mixins: [store, locale],
  components: { atyTreeNode, atyButton, autoComplete },
  provide() {
    return {
      tree: this
    }
  },
  props: {
    /**
     * #property
     *
     * @name    data
     * @cn      树的整个数据
     * @type    array,object
     * @bind    must
     * @description 树节点的数据，可以是一个对象或者数组。必须字段， {id:'',name:''}
     * 可配字段
     *  children: []  //  当有子节点时，需要用children字段
     *  disabled: true  //  该节点为禁用状态，不可操作
     *  leaf: true ,// boolean  是否叶子节点,
     *  showCheck: true ,// boolean  是否显示checkbox,
     *  checked: true  //  当有复选框时，checked控制该节点的选中与否
     *  icon: 'url'  //  当有图标时，icon控制其需要显示的图片，为图标路径
     *  open: true  //  控制节点默认是否展开
     *  draggable: true  //  该节点可拖动
     */
    data: {
      type: [Array, Object],
      default() {
        return []
      }
    },
    /**
     * #property
     *
     * @name    showCheckbox
     * @cn      是否显示多选框
     * @group   控制
     * @type    boolean
     * @value   false
     * @description 是否显示多选框
     */
    showCheckbox: {
      type: Boolean,
      default: false
    },
    /**
     * #property
     *
     * @name    showIcon
     * @cn      是否显示图标
     * @group   控制
     * @type    boolean
     * @value   false
     * @description 是否显示图标
     */
    showIcon: {
      type: Boolean,
      default: false
    },
    /**
     * #property
     *
     * @name    canMendName
     * @cn      是否可以修改名称
     * @group   控制
     * @type    boolean
     * @value   false
     * @description 是否可以修改名称
     */
    canMendName: {
      type: Boolean,
      default: false
    },
    /**
     * #property
     *
     * @name    cascade
     * @cn      级联选择
     * @group   控制
     * @type    boolean
     * @value   false
     * @description 复选框多选时，是否关联子节点选中
     */
    cascade: {
      type: Boolean,
      default: false
    },
    // 不加注释因为这个属性只给select-tree内部使用
    onlyLeaf: {
      type: Boolean,
      default: false
    },
    /**
     * #property
     *
     * @name    connectParent
     * @cn      关联父级选中
     * @group   控制
     * @type    boolean
     * @value   false
     * @description 复选框多选时，是否关联父级选中
     */
    connectParent: {
      type: Boolean,
      default: false
    },
    /**
     * #property
     *
     * @name    lazyLoad
     * @cn      是否可以懒加载数据
     * @group   控制
     * @type    boolean
     * @value   false
     * @description 是否可以懒加载数据
     */
    lazyLoad: {
      type: Boolean,
      default: false
    },
    /**
     * #property
     *
     * @name    syncRender
     * @cn      同步渲染
     * @group   控制
     * @type    boolean
     * @value   false
     * @description 当lazyLoad为false的情况此参数有效. syncRender为true时将同步渲染所有树节点, 当节点过多的时候会卡顿.
     * 后台返回的treeNode缺少parentId会导致搜索无法定位, 解决方案有三个(任选其一):
     * 1.lazyLoad=false,syncRender=false, 同时后台返回的treeNode必须包含parentId属性(推荐)
     * 2.remote=true,同时指定remoteMethod, 即自己实现远程搜索功能, 服务端返回的treeNode.id为根节点id到此id的路径, 逗号分隔(推荐)
     * 3.lazyLoad=false,syncRender=true, 同时后台一次性返回整棵树的节点(树节点比较少的情况可以用)
     */
    syncRender: {
      type: Boolean,
      default: false
    },
    /**
     * #property
     *
     * @name    draggable
     * @cn      是否可以拖拽
     * @group   控制
     * @type    boolean
     * @value   false
     * @description 是否可以拖拽
     */
    draggable: {
      type: Boolean,
      default: false
    },
    /**
     * #property
     *
     * @name    hasOperate
     * @cn      是否有操作按钮
     * @group   控制
     * @type    boolean
     * @value   false
     * @description 是否有操作按钮，默认有上移，下移和删除，可以自己通过 slot方法是朝里面添加自己的需要的按钮
     */
    hasOperate: {
      type: Boolean,
      default: false
    },
    /**
     * #property
     *
     * @name    canSearch
     * @cn      是否可以搜索定位
     * @group   控制
     * @type    boolean
     * @value   false
     * @description 是否可以搜索定位，能快速定位到某个节点
     */
    canSearch: {
      type: Boolean,
      default: false
    },
    /**
     * #property
     *
     * @name    pinyinSearch
     * @cn      是否支持拼音搜索
     * @group   控制
     * @type    boolean
     * @value  false
     * @description 是否支持拼音搜索,remoteMethod调用的后台需要返回搜索节点的'fullSpell'（汉语拼音全拼）和'simpleSpell'（汉语拼音简拼）数据
     */
    pinyinSearch: {
      type: Boolean,
      default: false
    },
    /**
     * #property
     *
     * @name    icon
     * @cn      图标icon
     * @group   控制
     * @type    icon
     * @value   ios-search-strong
     * @description 搜索框尾部的图标
     */
    icon: {
      type: String,
      default: 'ios-search-strong'
    },
    /**
     * #property
     *
     * @name    filter
     * @cn      搜索字段
     * @group   过滤
     * @type    string
     * @description 搜索框中的默认字段
     */
    filter: {
      type: String,
      default: ''
    },
    /**
     * #property
     *
     * @name    filterDelay
     * @cn      过滤延迟
     * @group   加载
     * @type    number
     * @value   600
     * @description 过滤延迟，默认是600毫秒,也就是触发 input-search 事件的延迟事件
     */
    filterDelay: {
      type: Number,
      default: 600
    },

    /**
     * #property
     *
     * @name    clearable
     * @cn      是否有清除按钮
     * @type    boolean
     * @description 是否有清除按钮
     */
    clearable: {
      type: Boolean,
      default: false
    },

    /**
     * #property
     *
     * @name    placeholder
     * @cn      占位文本
     * @type    string
     * @description 占位文本
     */
    placeholder: {
      type: String,
      default: '请输入需要查询的名称'
    },
    /**
     * #property
     *
     * @name    delay
     * @cn      延迟（毫秒数）
     * @type    number
     * @value   300
     * @bind    must
     * @description 延迟查询，默认300毫秒,远程搜索有用
     */
    delay: {
      type: Number,
      default: 300
    },
    /**
     * #property
     *
     * @name    remoteMethod
     * @cn      远程搜索的方法
     * @type    function
     * @bind    must
     * @description 远程搜索的方法；
     */
    remoteMethod: {
      type: Function
    },
    /**
     * #property
     *
     * @name    remoteUrl
     * @cn      远程搜索的url
     * @type    string
     * @description 远程搜索的url；
     */
    remoteUrl: {
      type: String,
      default: ''
    },
    /**
     * #property
     *
     * @name    remoteExtendsParams
     * @cn      远程搜索的额外参数
     * @type    object
     * @bind    must
     * @description 远程搜索的额外参数
     */
    remoteExtendsParams: {
      type: Object
    },
    /**
     * #property
     *
     * @name    autoRequestData
     * @cn      是否自动请求数据
     * @group   加载
     * @type    boolean
     * @value   false
     * @description 是否自动请求数据
     */
    autoRequestData: {
      type: Boolean,
      default: false
    },
    /**
     * #property
     *
     * @name    requestDataUrl
     * @cn      请求数据的url
     * @group   加载
     * @type    string
     * @description 请求数据的url
     */
    requestDataUrl: {
      type: String
    },

    /**
     * #property
     *
     * @name    requestDataMethod
     * @cn      请求数据的方法
     * @group   控制
     * @type    function
     * @bind    must
     * @description 请求数据的方法；
     * 触发懒加载数据时调用该函数，请注意，回调函数必须执行，否则会一直显示转圈，参数为新加载的数据,参数解释如下：
     *   1.id       当前的节点的id
     *   2.callback function  需要执行的回调函数，参数为新加载的数据（newData）！imporatnt
     *   3.tree    当前的树对象
     *   4.getAllChild 是否获取全部子孙节点(只有级联全选的情况会用到)
     *     function  requestDataMethod(id,callback,tree,getAllChild){ callback(newData) }
     */
    requestDataMethod: {
      type: Function
    },
    /**
     * #property
     *
     * @name    requestDataExtendsParams
     * @cn      请求数据的额外参数
     * @group   加载
     * @type    object
     * @bind    must
     * @description 请求数据的额外参数
     */
    requestDataExtendsParams: {
      type: Object
    },
    /**
     * #property
     *
     * @name    searchShowLimit
     * @cn      搜索框显示搜索结果数量限制
     * @group   加载
     * @type    number
     * @bind    must
     * @value   5
     * @description 搜索框显示搜索结果的数量限制,如果调大搜素框会出现滚动条可能影响效果展示
     */
    searchShowLimit: {
      type: Number,
      default: 5
    },
    /**
     * #property
     *
     * @name    positionDelay
     * @cn      定位延迟毫秒
     * @group   加载
     * @type    number
     * @bind    must
     * @value   300
     * @description 搜索定位延迟时间，默认300ms,太小在异步加载时容易计算错误位置
     */
    positionDelay: {
      type: Number,
      default: 300
    },
    /**
     * #property
     *
     * @name    canSelectDisabled
     * @cn      是否可选择无效的节点
     * @group   控制
     * @type    boolean
     * @value   false
     * @description 仅单选使用, 无效节点是否触发select
     */
    canSelectDisabled: {
      type: Boolean,
      default: false
    }
  },
  data() {
    return {
      prefixCls: prefixCls,
      currentData: [],
      // 正在加载
      loading: true
    }
  },
  watch: {
    /*
     * 不deep的监听就可以避免节点过多卡顿问题了 2019-7-25
     * 这样给data直接赋新值,调用push splice等都会响应
     * 如果修改内部节点的属性需要用Vue.$set()来实现
     */
    data(newValue) {
      this.resetInitData(newValue)
    }
  },
  computed: {
    wrapClass() {
      return [
        prefixCls + '-wrap'
      ]
    },
    treeClass() {
      return [
        prefixCls
      ]
    },
    searchClass() {
      return [
        prefixCls + '-search'
      ]
    },
    operateClass() {
      return [
        prefixCls + '-operate'
      ]
    },
    treeDragPreviewClass() {
      return [
        prefixCls + '-drag-preview'
      ]
    },
    treeDragLineClass() {
      return [
        prefixCls + '-drag-line'
      ]
    },
    treeDragContentClass() {
      return [
        prefixCls + '-drag-content'
      ]
    },
    loadingWrapClass() {
      return [
        prefixCls + '-loading',
        {
          show: true
        }
      ]
    },
    loadingClass() {
      return [
        config.prefix + '-icon',
        config.prefix + '-icon-load-c',
        config.prefix + '-load-loop'
      ]
    },
    localeEmptyText() {
      return this.t('i.tree.emptyText')
    },
    // 显示正在加载
    showLoading() {
      return this.autoRequestData && this.loading
    },
    // 显示空数据
    showEmpty() {
      return !this.showLoading && !this.currentData.length
    },
    frontAsync() { // 前端异步渲染
      return !this.lazyLoad && !this.syncRender
    }
  },

  methods: {
    /**
     * #method
     *
     * @name   getActiveId
     * @returns  currentActiveId
     * @description    获取当前激活的树节点的id
     */
    getActiveId() {
      return this.currentActiveId
    },
    /**
     * #method
     *
     * @name   setActiveId
     * @param   id  {string}
     * @description    设置当前激活的树节点的id
     */
    setActiveId(id) {
      if (id !== this.currentActiveId) {
        // 当前激活的id
        this.currentActiveId = id
        // 当前的节点赋值
        this.currentActiveTreeNode = this.getTreeNode(this.currentActiveId)
        if (!this.showCheckbox) {
          // 触发 watch-update
          this.$emit('watch-update', false, this.currentActiveTreeNode) // 这个事件是给下拉树用的, 用于选中结果更新到输入框中
        }
      }
    },
    /**
     * #method
     *
     * @name   getActiveTreeNode
     * @returns  currentActiveTreeNode
     * @description    获取当前激活的树节点
     */
    getActiveTreeNode() {
      // return this.getTreeNode(this.currentActiveId)
      return this.currentActiveTreeNode
    },
    /**
     * #method
     *
     * @name   getCheckedIds
     * @returns  currentCheckedIds
     * @description    获取当前所有已选中的树节点的id集合
     */
    getCheckedIds() {
      return this.currentCheckedIds
    },
    // 重置数据
    resetInitData(data) {
      if (!data) {
        return
      }
      if (!Array.isArray(data)) {
        data = [data]
      }
      // if (data.length == 0) {
      //   return // 外部使用的时候data都是异步赋值, 所以开始的时候是没有值的, 不需要走下面逻辑
      // }
      this.currentData = this.renderData(data)
      // this.currentData = data
      // this.updateCompleteOriginData() // TODO 这个耗费性能, 放在tree-node-add里去做, 看看会不会有问题
      // 重新渲染完成
      this.$nextTick(() => {
        // 支持搜索并且没有指定远程搜索方法, 则走本地的搜索, 初始化本地的allTreeNodeData
        // 指定了自定义url或者自定义方法, 也需要初始化本地的allTreeNodeData, 因为有可能直接返回全量的了
        if (!this.remoteMethod || this.requestDataUrl || this.requestDataMethod) {
          if (data.length > 0) {
            this.fillNodes(data)
          }
        }
        /**
         * #event
         *
         * @name    data-init
         * @param   data       数据
         * @body    console.log(data)
         * @version  1.0.3
         * @description  数据首次初始化完成事件
         */
        this.$emit('data-init', data)
      })
    },
    renderData(data) { // 获取应该渲染的数据
      let renderData = data
      if (!this.draggable && this.frontAsync) { // 非懒加载 前端内部实现为懒加载, 即每次只渲染能看到的node
        renderData = data.map(d => {
          const copyData = this.copyNodeData(d)
          if (d.open) { // 一级子节点是open的话 孙子节点是open 影响性能
            copyData.open = true
            copyData.children = d.children.map(cd => this.copyNodeData(cd))
          }
          return copyData
        })
      }
      return renderData
    },
    fillNodes(data, parentId) {
      if (!data || data.length == 0) {
        return
      }
      data.forEach(item => {
        this._allTreeNodeData[item.id] = item
        if (!this.syncRender && parentId) {
          item.parentId = parentId
        }
        this.fillNodes(item.children, item.id)
      })
    },
    removeNodes (id) {
      const node = this._allTreeNodeData[id]
      if (!node) {
        return
      }
      delete this._allTreeNodeData[id]
      if (node.children && node.children.length) {
        node.children.forEach((child) => {
          this.removeNodes(child.id)
        })
      }
    },
    copyNodeData(data) { // 拷贝数据, 不含children
      let copy = {children: []}
      for (let key in data) {
        if (key !== 'children') {
          copy[key] = data[key]
        }
      }
      if (copy.leaf === undefined) {
        copy.leaf = !(data.children && data.children.length)
      }
      return copy
    },
    /**
     * @name  getCurrentActiveTreeNodeIndexInfo
     * @returns  {indexArray,len,currentIndex,currentDataParent}  {object}
     * @description   获取目标index中的相关信息
     */
    getCurrentActiveTreeNodeIndexInfo() {
      let _indexArray = this.getActiveTreeNode().index.split('-')
      //alert(this.currentActiveTreeNode.index)
      let _len = _indexArray.length
      let _currentIndex = ~~_indexArray[_len - 1]
      let _currentDataParent = this.currentData
      // 获取当前的数据所在的父数组
      _indexArray.forEach((item, index) => {
        if (index != _len - 1) {
          _currentDataParent = _currentDataParent[item].children
        }
      })
      return {
        // index以“-”拆分后形成的数组
        indexArray: _indexArray,
        // 数组的长度
        len: _len,
        // 目标的index
        currentIndex: _currentIndex,
        // 当前的数据，所在的数组
        currentDataParent: _currentDataParent
      }
    },
    // 处理数据
    operateData(command, callback) {
      if (!this.currentActiveId) {
        alert('请先选中一条数据')
        return
      }
      let {indexArray, len, currentIndex, currentDataParent} = this.getCurrentActiveTreeNodeIndexInfo()
      let _data = null
      switch (command) {
        case 'move-up':
          //  如果当前的值为0
          if (currentIndex == 0) {
            // 如果长度为0，那么就是根节点
            if (len == 1) {
              alert('不允许移动根节点')
            } else {
              alert('已经到第一个了')
            }
            return false
          }
          // 先删除
          _data = currentDataParent.splice(currentIndex, 1)
          // 添加数据
          currentDataParent.splice(currentIndex - 1, 0, _data[0])
          break
        case 'move-down':
          //  如果当前的值为0
          if (currentIndex == currentDataParent.length - 1) {
            alert('已经到做后一个了')
            return false
          }
          // 先删除
          _data = currentDataParent.splice(currentIndex, 1)
          // 再添加
          currentDataParent.splice(currentIndex + 1, 0, _data[0])
          break
        case 'delete':
          // 删除数据
          _data = currentDataParent.splice(currentIndex, 1)
          this.currentActiveTreeNode = null
          this.currentActiveId = ''
          break
        default :
          break
      }
      callback(_data[0], this.currentActiveTreeNode)
    },
    // 上移动
    clickMoveUp() {
      this.operateData('move-up', (data, treeNode) => {
        /**
         * #event
         *
         * @name    move-up
         * @param   data       当前移动的数据
         * @param   treeNode   当前移动的节点
         * @body
         * @description    点击上移按钮触发
         */
        this.$emit('move-up', data, treeNode)
      })
    },
    // 下移动
    clickMoveDown() {
      this.operateData('move-down', (data, treeNode) => {
        /**
         * #event
         *
         * @name    move-down
         * @param   data       当前移动的数据
         * @param   treeNode   当前移动的节点
         * @body
         * @description    点击下移按钮触发
         */
        this.$emit('move-down', data, treeNode)
      })
    },
    // 删除数据
    clickDelete() {
      this.operateData('delete', (data, treeNode) => {
        this.$nextTick(() => {
          /**
           * #event
           *
           * @name    delete
           * @param   data       当前删除的数据
           * @param   treeNode   当前删除的节点
           * @body
           * @description    点击删除按钮触发
           */
          this.$emit('delete', data, treeNode)
        })
      })
    },

    // 本地搜索，改变获得焦点的树节点
    localChangeFocusTreeNode(id, callback) {
      // 无需判断存在性，因为肯定存在，数据是根据树节点传进去的
      const _this = this
      const treeNode = this.getTreeNode(id)
      if (!treeNode) {
        callback()
        return
      }
      // 该元素获得焦点
      treeNode.isFocus = true
      // 重新赋值
      this.focusTreeNode = treeNode
      // 找到标题元素 ， treeNodeHeadRef的指向
      const _el = this.focusTreeNode.$refs.treeNodeHeadRef
      // 查找所有的父级组件
      const _parents = Artery.findComponentsUpward(this.focusTreeNode, prefixCls + '-node')
      // 如果父级组件大于0
      if (_parents.length > 0) {
        _parents.forEach(function (item, index) {
          // 需要判断其是否是展开状态，如果不是，那么需要将其展开
          if (!item.open) {
            item.open = true
          }
        })
      }
      // 等vue dom更新完以后，执行回调函数
      _this.$nextTick(() => {
        setTimeout(() => {
          // 设置滚动条的位置
          Artery.setScrollBarPosition(_el, _this.$refs.treeContain)
        }, _this.positionDelay)

      })
      callback(treeNode)
    },
    // 远程搜索，改变获得焦点的树节点
    remoteChangeFocusTreeNode(id, callback) {
      const _this = this
      // !important  特别注意，数据id必须是逗号分隔
      // !important  特别注意，数据id必须是逗号分隔
      // !important  特别注意，数据id必须是逗号分隔
      let _idArray = id.split(',')
      let _id = _idArray[_idArray.length - 1]
      // 如果所有的树节点中包含了查询的节点，那么不需要再请求数据
      if (this.getTreeNode(_id)) {
        // 存在的情况，那么就已经到本地了
        this.localChangeFocusTreeNode(_id, callback)
      } else {
        // 显示加载
        this.adjustLoading(true)
        // 查询数据
        this.remoteRequestTreeNodeData(_idArray).then(data => {
          //  dom渲染完以后定位到目标位置
          _this.$nextTick(() => {
            // 显示加载
            _this.adjustLoading(false)
            // 存在的情况，那么就已经到本地了
            _this.localChangeFocusTreeNode(_id, callback)
          })
        }).catch(error => {
          _this.adjustLoading(false)
          callback()
          console.error('请求数据失败,error=' + JSON.stringify(error))
        })
      }
    },
    serialRequestTreeNodeData(_ids, index, resolve, reject) { //串行请求，防止取不到上级节点数据
      // let _len = _ids.length
      // let requestIds = _ids.slice(0, _len - 1)
      if (_ids.length - 2 < index) {
        resolve()
        return
      }
      let curr = this.getTreeNode(_ids[index])
      if (curr.treeNodeData.children && curr.treeNodeData.children.length > 0) {
        this.serialRequestTreeNodeData(_ids, index + 1, resolve, reject)
        return
      }
      this.requestTreeData(_ids[index], data => {
        if (data) {
          this.$set(curr.treeNodeData, 'children', data)
          if (this.canSearch) {
            for (let i = 0; i < data.length; i++) {
              this._allTreeNodeData[data[i].id] = data[i]
              if (!this.syncRender) {
                data[i].parentId = _ids[index]
              }
            }
          }
          this.$nextTick(() => this.serialRequestTreeNodeData(_ids, index + 1, resolve, reject))
        }
      })
    },
    // 远程请求树节点数据
    remoteRequestTreeNodeData(_shouldRequestIds) {
      return new Promise((resolve, reject) => {
        this.serialRequestTreeNodeData(_shouldRequestIds, 0, resolve, reject)
      })
    },
    hideFocusAndClearSearch() {
      this.hideFocus()
      let searchAutoComplete = this.$refs.searchAutoComplete
      if (searchAutoComplete) {
        searchAutoComplete.clear()
        searchAutoComplete.currentData = []
      }
    },
    // 隐藏焦点
    hideFocus() {
      // 如果存在获得焦点的元素
      if (this.focusTreeNode) {
        // 设置为false
        this.focusTreeNode.isFocus = false
      }
    },
    // 改变获得焦点的树节点
    changeFocusTreeNode(id, callback) {
      // 隐藏焦点
      this.hideFocus()
      if (!this.lazyLoad && this.syncRender) {
        this.localChangeFocusTreeNode(id, callback)
      } else {
        this.remoteChangeFocusTreeNode(id, callback)
      }
      // // 如果是远程搜索
      // if (this.remote) {
      //   this.remoteChangeFocusTreeNode(id)
      // } else {
      //   this.localChangeFocusTreeNode(id)
      // }
    },
    // 搜索值发生变化
    changeQuery(value) {
      if (value.id) {
        //  设置获得焦点树节点
        this.changeFocusTreeNode(value.id)
      }
      /**
       * #event
       *
       * @name    input-search
       * @param   value      当前搜索框中的值对象
       * @body    console.log("搜索值为："+value)
       * @description    input中的值发生改变时触发
       */
      this.$emit('input-search', value)
    },
    // // 更新自动完成的源数据
    // updateCompleteOriginData() {
    //   if (!this.canSearch || this.remote) {
    //     // 不搜索 || 远程搜索, 不需要更新autoComplete候选数据
    //     return
    //   }
    //   if (this.completeOriginDataTimer) {
    //     clearTimeout(this.completeOriginDataTimer)
    //   }
    //   this.completeOriginDataTimer = setTimeout(() => { // TODO 4.这个需要遍历所有已存在节点,是否延迟或换一种实现
    //     // 赋值新数据
    //     this.completeOriginData = Object.values(this._allTreeNodeData).map((item) => {
    //       return {
    //         id: item.treeNodeData.id,
    //         name: item.treeNodeData.name,
    //         simpleSpell: item.treeNodeData.simpleSpell,
    //         fullSpell: item.treeNodeData.fullSpell
    //       }
    //     })
    //     //   console.log('结束一次哦')
    //     // 感觉100毫秒
    //   }, 100)
    // },
    adjustLoading(flag, delay = 0) {
      if (!flag) {
        setTimeout(() => {
          // 正在加载数据
          this.loading = flag
        }, delay)
      } else {
        // 正在加载数据
        this.loading = flag
      }
    },
    // 请求树的数据
    requestTreeData(id, callback, nodeData, getAllChild = false) {
      // 先判断前台是否有全量的数据了
      const treeNodeData = this.getTreeNodeData(id)
      if (treeNodeData && treeNodeData.children && treeNodeData.children.length) {
        callback(this.renderData(treeNodeData.children))
      } else if (this.requestDataMethod) { // 用户是否自己写了请求数据的方法
        // 执行请求数据的方法(用户自定义加载数据的方法)
        this.requestDataMethod(id, callback, this, getAllChild)
      } else if (!Artery.isServer && this.requestDataUrl) { // 那么必须先指定url
        // 请求参数
        let _params = Object.assign({id: id ? id : '', getAllChild: getAllChild}, this.requestDataExtendsParams || {})
        if (nodeData && nodeData.customData && nodeData.customData.type) { //兼容组织机构的参数
          _params.type = nodeData.customData.type
        }
        // 输出日志
        // Artery.consoleLogRequest(this.showLog, this.name, 'get', this.requestDataUrl, _params)
        // 请求数据
        Artery.ajax.get(this.requestDataUrl, {params: _params}).then(data => {
          // 执行回调函数
          callback(data)
          // 输出日志
          // Artery.consoleLogResponse(this.showLog, this.name, data)
        }, error => {
          // 请求失败
          callback(false)
          // 输出日志
          // Artery.consoleLogResponse(this.showLog, this.name)
        })
      } else if (!this.draggable && this.frontAsync) { // 前端实现的异步渲染 2019-11-12
        let data = this.recursiveFindData(this.data, id)
        if (!data) {
          callback([])
          return
        }
        let currentData = data.children
        if (!getAllChild) {
          currentData = data.children.map(cd => this.copyNodeData(cd))
        }
        callback(currentData)
      } else {
        console.error('自动请求数据必须指定请求地址：requestDataUrl')
        // 请求失败
        callback(false)
      }
    },
    recursiveFindData(sourceData, id) {
      if (sourceData && sourceData.length) {
        for (let i = 0; i < sourceData.length; i++) {
          if (sourceData[i].id === id) {
            return sourceData[i]
          }
          let data = this.recursiveFindData(sourceData[i].children, id)
          if (data) {
            return data
          }
        }
      } else {
        return null
      }
    },
    /**
     * #method
     *
     * @name   reload
     * @description    重置, 恢复为初始化状态
     */
    reload() {
      //  如果自动请求数据
      if (this.autoRequestData) {
        // 初始化请求数据
        this.initRequestTreeData()
      } else {
        // 初始化数据
        // this.resetStore()
        this.resetInitData(this.data)
      }
    },
    /**
     * #method
     *
     * @name   update
     * @param   data {array||object}   当前的树的整个数据
     * @description  更新当前树的整个数据, 使用数据驱动的方式给:data赋新值也可以
     */
    update(data) { // 直接给data赋新值也可以
      if (data !== undefined) {
        this.resetInitData(data)
      } else {
        this.resetInitData(this.data)
      }
    },
    // // 绑定事件
    bindEvent() {
      const _this = this
      //   //  绑定添加树节点事件
      //   _this.$on('tree-node-add', (treeNode) => {
      //     // 记录所有的tree
      //     _this._allTreeNodeData[treeNode.treeNodeData.id] = treeNode
      //     //可以搜索的情况下，并且非远程搜索，才需要自动完成的数据
      //     //  远程搜索全部走ajax，所以不用记录数的数据
      //     // 提升性能
      //     if (this.canSearch && !this.remote) {
      //       //更新自动完成的源数据
      //       // _this.updateCompleteOriginData()// TODO 5.每个节点创建后都会触发这个事件, 这里会频繁更新搜索数据
      //       _this.completeOriginData.push({
      //         id: treeNode.treeNodeData.id,
      //         name: treeNode.treeNodeData.name,
      //         simpleSpell: treeNode.treeNodeData.simpleSpell,
      //         fullSpell: treeNode.treeNodeData.fullSpell
      //       })
      //     }
      //   })
      //  绑定销毁树节点事件
      _this.$on('tree-node-destroy', function (id) {
        if (_this.showCheckbox) {
          let _index = _this.currentCheckedIds.indexOf(id)
          if (_index != -1) {
            // 在选中id中删除被删除的选中id
            _this.currentCheckedIds.splice(_index, 1)
            // 从所有选中节点集合中删除被删除的节点
            // delete _this.currentCheckedNodes[id]
          }
        } else if (id === this.currentActiveId) {
          this.currentActiveTreeNode = null
        }
        // 从总节点中删除节点及其子节点
        this.removeNodes(id)
        // if (this.canSearch && !this.remote) {
        //   //更新自动完成的源数据
        //   // _this.updateCompleteOriginData()// TODO 6.同5
        //   _this.completeOriginData.splice(_this.completeOriginData.findIndex(item => item.id === id), 1)
        // }
      })
    },
    // // 销毁事件
    unbindEvent() {
      // this.$off('tree-node-add')
      this.$off('tree-node-destroy')
    },
    // 初始化请求树数据
    initRequestTreeData() {
      let _this = this
      // 显示加载
      this.adjustLoading(true)
      this.requestTreeData('', function (data) {
        if (data) {
          // 如果是初始化加载树，那么就走初始化的方法。
          // _this.resetStore()
          _this.resetInitData(data)
          // 隐藏加载
          _this.adjustLoading(false)
        } else {
          // 隐藏加载,此处特意加的延时，仅为好看。
          _this.adjustLoading(false, 400)
        }
      })
    },
    /**
     * #method
     *
     * @name   getTreeNodeData
     * @param   id  {string}
     * @returns  treeNodeData
     * @description   获取树节点数据
     */
    getTreeNodeData(id) {
      if (!id) return null
      const treeNodeData = this._allTreeNodeData[id]
      if (treeNodeData) {
        return treeNodeData
      }
      if (this.frontAsync && this.data && this.data.length) {
        return this.recursiveFindData(this.data, id)
      }
      let treeNode = this.getTreeNode(id)
      return treeNode ? treeNode.treeNodeData : null
    },
    /**
     * 默认的远程搜索方法, 当用户没有提供自己的远程搜索的时候走这个
     */
    defaultRemoteMethod(query, callback) {
      if (!query) {
        return
      }
      // if (this.focusTreeNode) {
      //   this.focusTreeNode.isFocus = false
      // }
      // 优化性能, 只遍历到能显示的 2019-11-13
      const filterDatas = []
      for (let k in this._allTreeNodeData) {
        let treeNodeData = this._allTreeNodeData[k]
        if (treeNodeData.name.indexOf(query) !== -1) {
          filterDatas.push(treeNodeData)
          if (filterDatas.length === this.searchShowLimit) {
            break
          }
        }
      }
      const filterNodes = filterDatas.map(item => {
        let id = item.id
        if (!this.lazyLoad && this.syncRender) {
          // id = item.id
        } else if (item.parentId) {
          let pData = this.getTreeNodeData(item.parentId)
          while (pData && pData.id) {
            id = pData.id + ',' + id
            pData = this.getTreeNodeData(pData.parentId)
          }
        } else {
          console.warn("treeNodeData的parentId不存在, 搜索功能将无法定位")
        }
        return {
          id: id,
          name: item.name,
          simpleSpell: item.simpleSpell,
          fullSpell: item.fullSpell
        }
      })
      callback(filterNodes)
    }
  },

  created() {
    this._allTreeNodeData = {}
    // 绑定事件
    this.bindEvent()
    //  如果自动请求数据
    if (this.autoRequestData) {
      // 初始化请求数据
      this.initRequestTreeData()
    } else if (this.data.length) {
      this.resetInitData(this.data)
    }
  },
  mounted() {
    /**
     * #event
     *
     * @name    ready
     * @param   tree       当前的树对象
     * @body    console.log("ready")
     * @description    树插入到页面后触发
     */
    this.$emit('ready', this)
    // 这里必须保持在ready之后，所以加上了nextTick，不能用ready，因为ready会被多次触发
    this.$nextTick(() => {
      this.$emit('alreadyLoad', 'tree')
    })
  },
  // 销毁
  beforeDestroy() {
    // 销毁事件
    this.unbindEvent()
  }
}
